﻿/*
 * @Author: ws.s 
 * @Date: 2021-05-08 11:41:38 
 * @Last Modified by: ws.s
 * @Last Modified time: 2021-05-08 16:22:24
 */
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// 简单的状态机，需要使用Tick 驱动逻辑
///  2021-05-08 11:41:38  - wss 更新FSM状态机，支持IState接口，可以自己实现自己的 state 逻辑
/// </summary>
[System.Serializable]
public class FSM
{
    public interface IState
    {
        string Name { get; }
        void OnEnter();
        void OnUpdate();
        void OnExit();
    }

    [System.Serializable]
    public class State : IState
    {
        public State(string name, Action onEnter = null, Action onUpdate = null, Action onExit = null)
        {
            this.name = name;
            this.onEnter = onEnter;
            this.onUpdate = onUpdate;
            this.onExit = onExit;
        }

        public string name;
        private Action onUpdate;
        private Action onEnter;
        private Action onExit;
        public string Name => name;
        public void OnEnter()
        {
            onEnter?.Invoke();
        }
        public void OnUpdate()
        {
            onUpdate?.Invoke();
        }
        public void OnExit()
        {
            onExit?.Invoke();
        }
    }

    public FSM(bool debug = false)
    {
        this.debug = debug;
    }

    [SerializeField]
    private List<IState> fsmList = new List<IState>();

    public bool debug = false;

    public IState currentState { get => _currentState; }
    public IState preState { get; }
    [SerializeField] private IState _currentState;
    private IState _preState;
    [HideInInspector]
    public float duration = 0;
    [HideInInspector]
    public bool paused = false;

    public Dictionary<string, Action> callbackDic = new Dictionary<string, Action>();

    public void AddState(string name, Action onEnter = null, Action onUpdate = null, Action onExit = null)
    {
        this.AddState(new State(name, onEnter, onUpdate, onExit));
    }

    public void AddState(IState state)
    {
        var _tempState = this.GetState(state);
        if (_tempState == null)
        {
            this.fsmList.Add(state);
            if (this.fsmList.Count == 1)
            {
                this._currentState = state;
            }
        }
        else
        {
            Debug.LogWarning($"FSM:Had added {state.Name}");
        }
    }

    public void AddStates(IState[] states)
    {
        foreach (var item in states)
        {
            this.AddState(item);
        }
    }

    public bool CompareState(IState state)
    {
        return this._currentState == state;
    }


    public bool CompareState(string state)
    {
        return this._currentState.Name == state;
    }

    public IState GetState(string stateName)
    {
        return this.fsmList.Find(state => state.Name == stateName);
    }

    public IState GetState(IState state)
    {
        // state = state.ToLower();
        if (this.fsmList.IndexOf(state) != -1)
        {
            return state;
        }
        else
        {
            return null;
        }
    }

    public void ResetState()
    {
        this.ChangeState(this._currentState);
    }

    private string GetStringUpper(string s)
    {
        return s.Substring(0, 1).ToUpper() + s.Substring(1);
    }

    public void Tick(float timer = -1)
    {
        if (timer == -1) timer = Time.deltaTime;
        if (this.paused || timer == 0) return;

        if (this._currentState != null)
        {
            this.duration += Time.deltaTime;
            this._currentState.OnUpdate();
        }
    }

    public void ChangeState(string state)
    {
        var _tmpState = this.GetState(state);
        this.ChangeState(_tmpState);
    }

    public void ChangeState(IState state)
    {
        if (state == null)
        {
            Debug.LogWarning($"FSM:STATE IS NULL");
            return;
        }
        var _tmpState = this.GetState(state);
        if (_tmpState == null)
        {
            Debug.LogWarning($"FSM:STATE {state.Name} NOT IN LIST");
        }

        if (this._currentState != null)
        {
            this._currentState.OnExit();
        }
        this._preState = this._currentState;
        this._currentState = _tmpState;
        this.duration = 0;
        this._currentState.OnEnter();

        if (this.debug)
        {
            Debug.Log($"{this._preState.Name} > {this._currentState.Name}");
        }
    }

    public void SendMessage(string function)
    {
        if (callbackDic.ContainsKey(function))
        {
            callbackDic[function]?.Invoke();
        }
    }

    public void RemoveAllState()
    {
        if (this._currentState != null)
        {
            this._currentState.OnExit();
            this._currentState = null;
        }
        this.fsmList.Clear();
    }
}
